﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using LinqToAmazonSL;

namespace SilverlightApplication.UIControls.Carousel
{
    public partial class Carousel : Grid
    {
        public ObservableCollection<AmazonItem> DisplayableItems;

        private int FirstDisplayed;
        private int LastDisplayed;
        private int FirstSpotTaken;
        private int LastSpotTaken;
        private int DisplayingItemsCount;
        private int SpotStoryboardsPlaying;
        private Direction MovingDirection;

        private int MovesPending;

        private Stack<CarouselItem> CarouselItemPool;
        private CarouselItem[] Spots;
        
        private Duration duration = new Duration(TimeSpan.FromSeconds(.33));
        
        private const int CarouselSpotCount = 15; // odd number!

        private Grid OpaqueCover;
        private ItemDisplayer itemDisplayer;
        private Polygon leftArrow;
        private Polygon rightArrow;

        private const double VIEWPORT_WIDTH = 480;
        private const double CAROUSEL_Y_OFFSET = 15;
        private const double ARROWS_Y_OFFSET = 40;
        private const double ITEM_DISPLAYER_Y_OFFSET = 0;

        public int DisplayingItemResultNumber
        {
            get
            {
                CarouselItem item = Spots[CarouselSpotCount / 2];

                if (item == null)
                    return 0;
                else
                    return item.ResultNumber;
            }
        }

        public bool DetailedDisplaying { get; set; }

        private int totalResultCount;

        public int TotalResultCount
        {
            get { return totalResultCount; }
            set { totalResultCount = value; }
        }

        public delegate void DisplayingItemChanged();
        public event DisplayingItemChanged displayingItemChanged;
       
        public Carousel()
        {
            this.Loaded += new RoutedEventHandler(CarouselLoaded);
            this.SizeChanged += new SizeChangedEventHandler(Carousel_SizeChanged);

            // Create the Pool of CarouselItem
            CarouselItemPool = new Stack<CarouselItem>();
            for (int i = 0; i < CarouselSpotCount; i++)
            {
                CarouselItem p = new CarouselItem();
                p.Name = "CarouselItem" + i;
                p.carouselItemClick += new CarouselItem.CarouselItemClick(p_carouselItemClick);
                p.carouselItemEnter += new CarouselItem.CarouselItemEnter(p_carouselItemEnter);
                p.carouselItemLeave += new CarouselItem.CarouselItemLeave(p_carouselItemLeave);
                CarouselItemPool.Push(p);

                // Enlarge Storyboard
                Storyboard stbd = new Storyboard();
                p.Resources.Add("Enlarge", stbd);
                DoubleAnimation da = new DoubleAnimation();
                stbd.Children.Add(da);
                da.Duration = new Duration(TimeSpan.FromSeconds(.1)); ;
                Storyboard.SetTargetProperty(da, new PropertyPath("Scale"));
                Storyboard.SetTarget(stbd, p);
                da.To = 1.05;

                // Shrink Storyboard
                stbd = new Storyboard();
                p.Resources.Add("Shrink", stbd);
                da = new DoubleAnimation();
                stbd.Children.Add(da);
                da.Duration = new Duration(TimeSpan.FromSeconds(.1)); ;
                Storyboard.SetTargetProperty(da, new PropertyPath("Scale"));
                Storyboard.SetTarget(stbd, p);
                da.To = 1;
            }

            // Create the Spots Array
            Spots = new CarouselItem[CarouselSpotCount];
            for (int i = 0; i < CarouselSpotCount; i++)
            {
                Spots[i] = null;
            }

            InitializeVariables();
            
            // Create Empty CarouselItem collection
            DisplayableItems = new ObservableCollection<AmazonItem>();

            DetailedDisplaying = false;

            // Create OpaqueCover
            OpaqueCover = new Grid();
            OpaqueCover.Background = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0));
            OpaqueCover.Opacity = 0;
            Canvas.SetZIndex(OpaqueCover, 200);
            this.Children.Add(OpaqueCover);
            OpaqueCover.Visibility = Visibility.Collapsed;

            // Create ItemSelected Storyboard
            Storyboard sb = new Storyboard();
            this.Resources.Add("ItemSelected", sb);

            DoubleAnimation das;

            Duration d = new Duration(TimeSpan.FromSeconds(0.6));

            // Yaw
            das = new DoubleAnimation();
            das.To = 360 * 1 + 180;
            das.Duration = d;
            Storyboard.SetTargetProperty(das, new PropertyPath("Yaw"));
            sb.Children.Add(das);

            // Y
            das = new DoubleAnimation();
            das.To = ITEM_DISPLAYER_Y_OFFSET;
            das.Duration = d;
            Storyboard.SetTargetProperty(das, new PropertyPath("Y"));
            sb.Children.Add(das);

            DoubleAnimationUsingKeyFrames daukf = new DoubleAnimationUsingKeyFrames();
            DoubleKeyFrame dkf1 = new LinearDoubleKeyFrame();
            dkf1.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.3));
            dkf1.Value = 0.3;
            DoubleKeyFrame dkf2 = new LinearDoubleKeyFrame();
            dkf2.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.6));
            dkf2.Value = 1.3;
            daukf.KeyFrames.Add(dkf1);
            daukf.KeyFrames.Add(dkf2);
            Storyboard.SetTargetProperty(daukf, new PropertyPath("Scale"));
            sb.Children.Add(daukf);

            // Create FadingBackground Storyboard
            sb = new Storyboard();
            this.Resources.Add("FadingBackground", sb);

            das = new DoubleAnimation();
            das.To = 0.3;
            das.BeginTime = TimeSpan.FromSeconds(0.6);
            das.Duration = new Duration(TimeSpan.FromSeconds(0.2));
            Storyboard.SetTargetProperty(das, new PropertyPath("Opacity"));
            Storyboard.SetTarget(sb, OpaqueCover);
            sb.Children.Add(das);

            // Left Arrow
            Polygon triangle = new Polygon();
            triangle.Fill = new SolidColorBrush(Colors.Orange);
            triangle.Cursor = Cursors.Hand;
            triangle.Points = new PointCollection();
            triangle.Points.Add(new Point(15, 0));
            triangle.Points.Add(new Point(0, 20));
            triangle.Points.Add(new Point(15, 40));
            triangle.Margin = new Thickness(10);
            triangle.VerticalAlignment = VerticalAlignment.Center;
            triangle.HorizontalAlignment = HorizontalAlignment.Left;
            triangle.MouseLeftButtonUp += new MouseButtonEventHandler(leftArrow_MouseLeftButtonUp);
            triangle.Visibility = Visibility.Collapsed;
            this.Children.Add(triangle);
            this.leftArrow = triangle;

            TranslateTransform tt = new TranslateTransform();
            tt.Y = ARROWS_Y_OFFSET;
            triangle.RenderTransform = tt;

            // Right Arrow
            triangle = new Polygon();
            triangle.Fill = new SolidColorBrush(Colors.Orange);
            triangle.Cursor = Cursors.Hand;
            triangle.Points = new PointCollection();
            triangle.Points.Add(new Point(0, 0));
            triangle.Points.Add(new Point(15, 20));
            triangle.Points.Add(new Point(0, 40));
            triangle.Margin = new Thickness(10);
            triangle.VerticalAlignment = VerticalAlignment.Center;
            triangle.HorizontalAlignment = HorizontalAlignment.Right;
            triangle.MouseLeftButtonUp += new MouseButtonEventHandler(rightArrow_MouseLeftButtonUp);
            triangle.Visibility = Visibility.Collapsed;
            this.Children.Add(triangle);
            this.rightArrow = triangle;
            triangle.RenderTransform = tt;

            // ItemDisplayer
            itemDisplayer = new ItemDisplayer();
            itemDisplayer.Opacity = 0;
            Children.Add(itemDisplayer);
            Canvas.SetZIndex(itemDisplayer, 201);
            itemDisplayer.Cursor = Cursors.Hand;
            itemDisplayer.MouseLeftButtonUp += new MouseButtonEventHandler(itemDisplayer_MouseLeftButtonUp);
            itemDisplayer.Visibility = Visibility.Collapsed;

            // Create ItemDisplayerFadeIn Storyboard
            sb = new Storyboard();
            this.Resources.Add("ItemDisplayerFadeIn", sb);

            das = new DoubleAnimation();
            das.To = 1;
            das.BeginTime = TimeSpan.FromSeconds(0.6);
            das.Duration = new Duration(TimeSpan.FromSeconds(0.2));
            sb.Children.Add(das);
            Storyboard.SetTargetProperty(das, new PropertyPath("Opacity"));
            Storyboard.SetTarget(sb, itemDisplayer);

            // Create ItemDisplayerFadeOut Storyboard
            sb = new Storyboard();
            this.Resources.Add("ItemDisplayerFadeOut", sb);
            das = new DoubleAnimation();
            das.To = 0;
            das.Duration = new Duration(TimeSpan.FromSeconds(0.1));
            sb.Children.Add(das);
            Storyboard.SetTargetProperty(das, new PropertyPath("Opacity"));
            Storyboard.SetTarget(sb, itemDisplayer);
            sb.Completed += delegate
            {
                itemDisplayer.Visibility = Visibility.Collapsed;
            };
        }

        void Carousel_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            double xRes = e.NewSize.Width;
            double yRes = e.NewSize.Height;

            double AspectRatio = 6 / 13d;
            double a = xRes * AspectRatio;
            double b = yRes;

            // Proportional Scaling
            if (a / b > 1)
                a = b;
            else
                b = a;

            itemDisplayer.DisplayZoomFactor = a * 0.0024;
        }

        void leftArrow_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Move(Direction.Left, 1);
        }

        void rightArrow_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Move(Direction.Right, 1);
        }

        void p_carouselItemLeave(CarouselItem senderItem)
        {
            senderItem.MouseLeft = true;
            if (senderItem.EnlargeCompleted == true)
            {
                BeginShrink(senderItem);
            }
        }

        void p_carouselItemEnter(CarouselItem senderItem)
        {
            BeginEnlarge(senderItem);

            senderItem.EnlargeCompleted = false;
            senderItem.MouseLeft = false;
        }

        void BeginShrink(CarouselItem c)
        {
            Storyboard stbd = (Storyboard)c.Resources["Shrink"];
            DoubleAnimation da = (DoubleAnimation)stbd.Children[0];
            da.To = Scale(Spot(c));
            stbd.Begin();
        }


        void BeginEnlarge(CarouselItem c)
        {
            Storyboard stbd = (Storyboard)c.Resources["Enlarge"];
            DoubleAnimation da = (DoubleAnimation)stbd.Children[0];
            da.To = Scale(Spot(c)) * 1.05;
            stbd.Begin();

            stbd.Completed += delegate(object sender, EventArgs e)
            {
                c.EnlargeCompleted = true;
                if (c.MouseLeft == true)
                {
                    BeginShrink(c);
                }
            };
        }

        void p_carouselItemClick(CarouselItem senderItem)
        {
            int i = Spot(senderItem);

            if (i < CarouselSpotCount / 2)
            {
                Move(Direction.Left, CarouselSpotCount / 2 - i);
            }
            else if (i > CarouselSpotCount / 2)
            {
                Move(Direction.Right, i - CarouselSpotCount / 2);
            }
            else
            {
                senderItem.EnlargeCompleted = false;
                senderItem.MouseLeft = false;
                BeginSelectItem(senderItem);
                itemDisplayer.DisplayingData = senderItem.Data;
            }
        }

        void BeginSelectItem(CarouselItem c)
        {
            Storyboard sb = (Storyboard)this.Resources["ItemSelected"];
            sb.Stop();
            Storyboard.SetTarget(sb, c);
            sb.Begin();

            OpaqueCover.Visibility = Visibility.Visible;

            sb = (Storyboard)this.Resources["FadingBackground"];
            sb.Begin();

            itemDisplayer.Visibility = Visibility.Visible;
            sb = (Storyboard)this.Resources["ItemDisplayerFadeIn"];
            sb.Begin();

            DetailedDisplaying = true;
        }

        void itemDisplayer_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            // Instantly restore the background
            Storyboard sb = (Storyboard)this.Resources["FadingBackground"];
            sb.Stop();
            OpaqueCover.Visibility = Visibility.Collapsed;

            // Put the center Slide back to its place
            CarouselItem displayedItem = Spots[CarouselSpotCount / 2];
            BeginStoryboard("MoveTo" + CarouselSpotCount / 2, displayedItem);

            // Fade the ItemDisplayer Out
            sb = (Storyboard)this.Resources["ItemDisplayerFadeOut"];
            sb.Begin();

            DetailedDisplaying = false;
         }

        // Returns the Spot the CarouselItem is located in
        int Spot(CarouselItem c)
        {
            for (int i = 0; i < CarouselSpotCount; i++)
            {
                if (Spots[i] == c) return i;
            }

            return -1;
        }

        void CarouselLoaded(object sender, RoutedEventArgs e)
        {
            // Create Storyboards for each position in the Carousel
            for (int i = 0; i < CarouselSpotCount; i++)
            {
                CreateStoryboard("MoveTo" + i, FormulaX(i), CAROUSEL_Y_OFFSET, OpacityFormula(i), Scale(i), Pitch(i), 0, 0, duration);
            }

            // Register the CollectionChanged Event
            DisplayableItems.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(DisplayableItems_CollectionChanged);
        }

        void DisplayableItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            {
                int freeSpotsRightCount;
                int addedItemsCount = e.NewItems.Count;

                if (DisplayingItemsCount == 0) // If there were no items yet
                {
                    freeSpotsRightCount = CarouselSpotCount / 2 + 1;
                    FirstDisplayed = 0;
                    FirstSpotTaken = CarouselSpotCount / 2;
                    LastSpotTaken = CarouselSpotCount / 2 - 1;
                }
                else
                {
                    freeSpotsRightCount = CarouselSpotCount - LastSpotTaken - 1;
                }

                int planesToAddCount = Math.Min(addedItemsCount, freeSpotsRightCount);

                // Add planes for new items
                for (int i = 0; i < planesToAddCount; i++)
                {
                    CarouselItem c = CarouselItemPool.Pop();

                    // Load the plane with the Result
                    AttachResultItem(c, (AmazonItem)e.NewItems[i]);
                    LastDisplayed++;

                    // Set the Plane in the next free spot
                    PositionInSpot(c, LastSpotTaken + 1);
                    this.Children.Add(c);
                    Spots[LastSpotTaken + 1] = c;
                    LastSpotTaken++;

                    DisplayingItemsCount++;
                }
            }
            else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset)
            {
                CarouselItem item;

                // Remove the CarouselItems from the Carousel
                for (int i = 0; i < CarouselSpotCount; i++)
                {
                    item = Spots[i];
                    if (item != null)
                    {
                        DettachResultItem(item);
                        this.Children.Remove(item);
                        CarouselItemPool.Push(item);
                        Spots[i] = null;
                    }
                }
            }
            else
                new Exception("Invalid collection Action");

            CheckArrowDrawing();
        }

        void AttachResultItem(CarouselItem cItem, AmazonItem aItem)
        {
            cItem.Data = aItem;
            cItem.ResultNumber = DisplayableItems.IndexOf(aItem) + 1;
        }

        void DettachResultItem(CarouselItem cItem)
        {
            cItem.Data = null;
            cItem.ResultNumber = -1;
        }

        void MoveRight()
        {
            // The sequence of Items moves to the LEFT

            // If there's an item to move to
            if (LastSpotTaken > CarouselSpotCount / 2)
            {
                // If there's a leftmost element, return it's Plane3D
                if (FirstSpotTaken == 0)
                {
                    this.Children.Remove(Spots[0]);

                    DettachResultItem(Spots[0]);

                    // Return the CarouselItem
                    CarouselItemPool.Push(Spots[0]);
                    Spots[0] = null;
                    FirstDisplayed++;
                    FirstSpotTaken++;
                    DisplayingItemsCount--;
                }

                // Move the Items in the middle
                for (int i = FirstSpotTaken; i <= LastSpotTaken; i++)
                {
                    Spots[i - 1] = Spots[i];
                    BeginStoryboard("MoveTo" + (i - 1), Spots[i]);
                    Spots[i] = null;
                }
                FirstSpotTaken--;
                LastSpotTaken--;

                // If there's at least one more Item to show
                if (LastDisplayed + 1 < DisplayableItems.Count)
                {
                    CarouselItem p = CarouselItemPool.Pop();

                    // Load the plane with the Result
                    AttachResultItem(p, DisplayableItems[LastDisplayed + 1]);
                    LastDisplayed++;

                    // Set the Plane in the last spot
                    PositionInSpot(p, CarouselSpotCount - 1);
                    this.Children.Add(p);
                    Spots[CarouselSpotCount - 1] = p;
                    LastSpotTaken++;
                    DisplayingItemsCount++;
                }

                // Calculate Z
                for (int i = FirstSpotTaken; i <= LastSpotTaken; i++)
                {
                    Canvas.SetZIndex(Spots[i], CalculateZ(i));
                }
            }
        }

        void CheckArrowDrawing()
        {
            // Show/Hide Arrows

            // If not displaying last item
            if (DisplayingItemResultNumber < TotalResultCount)
                rightArrow.Visibility = Visibility.Visible;
            else
                rightArrow.Visibility = Visibility.Collapsed;

            // If not displaying first item
            if (DisplayingItemResultNumber > 1)
                leftArrow.Visibility = Visibility.Visible;
            else
                leftArrow.Visibility = Visibility.Collapsed;
        }

        void MoveLeft()
        {
            // The sequence of Items moves to the RIGHT

            // If there's an item to move to
            if (FirstSpotTaken < CarouselSpotCount / 2)
            {
                // If there's a rightmost element, return it's Plane3D
                if (LastSpotTaken == CarouselSpotCount - 1)
                {
                    this.Children.Remove(Spots[CarouselSpotCount - 1]);

                    DettachResultItem(Spots[CarouselSpotCount - 1]);

                    // Return the Plane3D
                    CarouselItemPool.Push(Spots[CarouselSpotCount - 1]);
                    Spots[CarouselSpotCount - 1] = null;
                    LastDisplayed--;
                    LastSpotTaken--;
                    DisplayingItemsCount--;
                }

                // Move the Items in the middle
                for (int i = LastSpotTaken; i >= FirstSpotTaken; i--)
                {
                    Spots[i + 1] = Spots[i];
                    BeginStoryboard("MoveTo" + (i + 1), Spots[i]);
                    Spots[i] = null;
                }
                FirstSpotTaken++;
                LastSpotTaken++;

                // If there's at least one more Item to show
                if (FirstDisplayed > 0)
                {
                    CarouselItem p = CarouselItemPool.Pop();

                    // Load the plane with the Result
                    AttachResultItem(p, DisplayableItems[FirstDisplayed - 1]);
                    FirstDisplayed--;

                    // Set the Plane in the last spot
                    PositionInSpot(p, 0);
                    this.Children.Add(p);
                    Spots[0] = p;
                    FirstSpotTaken--;
                    DisplayingItemsCount++;
                }

                // Calculate Z
                for (int i = FirstSpotTaken; i <= LastSpotTaken; i++)
                {
                    Canvas.SetZIndex(Spots[i], CalculateZ(i));
                }
            }
        }

        void PositionInSpot(CarouselItem c, int n)
        {
            if (n < 0 || n >= CarouselSpotCount)
                throw new Exception("Invalid Spot");

            c.Y = CAROUSEL_Y_OFFSET;
            c.X = FormulaX(n);
            c.Opacity = OpacityFormula(n);
            c.Scale = Scale(n);
            c.Pitch = Pitch(n);
            Canvas.SetZIndex(c, CalculateZ(n));
        }

        public void Move(Direction d, int spots)
        {
            if (DisplayableItems.Count != 0 && OpaqueCover.Visibility == Visibility.Collapsed)
            {
                if (SpotStoryboardsPlaying == 0)
                {
                    MovesPending = spots;
                    MovingDirection = d;

                    MoveCarousel();
                }
                else
                {
                    lock (typeof(int))
                    {
                        MovesPending += spots;
                    }
                }
            }
        }

        void MoveCarousel()
        {
            /// PRE: DisplayableItems.Count != 0
            if (MovesPending > 0)
            {
                if (MovingDirection == Direction.Left)
                    MoveLeft();
                else
                    MoveRight();

                CheckArrowDrawing();

                lock (typeof(int))
                {
                    MovesPending--;
                }

                if (displayingItemChanged != null)
                    displayingItemChanged();
            }
        }

        private Storyboard CreateStoryboard(string name, double tox, double toy, double too, double tos, double top, double toya, double tor, Duration duration)
        {
            Storyboard storyboard = new Storyboard();
            this.Resources.Add(name, storyboard);

            //x animation
            DoubleAnimation dax = new DoubleAnimation();
            storyboard.Children.Add(dax);
            dax.To = tox;
            dax.Duration = duration;
            Storyboard.SetTargetProperty(dax, new PropertyPath("X"));
            //y animation
            DoubleAnimation day = new DoubleAnimation();
            storyboard.Children.Add(day);
            day.To = toy;
            day.Duration = duration;
            Storyboard.SetTargetProperty(day, new PropertyPath("Y"));
            //opacity animation
            DoubleAnimation dao = new DoubleAnimation();
            storyboard.Children.Add(dao);
            dao.To = too;
            dao.Duration = duration;
            Storyboard.SetTargetProperty(dao, new PropertyPath("Opacity"));
            //pitch animation
            DoubleAnimation dap = new DoubleAnimation();
            storyboard.Children.Add(dap);
            dap.To = top;
            dap.Duration = duration;
            Storyboard.SetTargetProperty(dap, new PropertyPath("Pitch"));
            //yaw animation
            DoubleAnimation daya = new DoubleAnimation();
            storyboard.Children.Add(daya);
            daya.To = toya;
            daya.Duration = duration;
            Storyboard.SetTargetProperty(daya, new PropertyPath("Yaw"));
            //roll animation
            DoubleAnimation dar = new DoubleAnimation();
            storyboard.Children.Add(dar);
            dar.To = tor;
            dar.Duration = duration;
            Storyboard.SetTargetProperty(dar, new PropertyPath("Roll"));
            //scale animation
            DoubleAnimation dasx = new DoubleAnimation();
            storyboard.Children.Add(dasx);
            dasx.To = tos;
            dasx.Duration = duration;
            Storyboard.SetTargetProperty(dasx, new PropertyPath("Scale"));

            storyboard.Completed += delegate(object sender, EventArgs e)
                                    {
                                        string targetName = Storyboard.GetTargetName(storyboard);
                                        CarouselItem targetPlane = (CarouselItem)this.FindName(targetName);
                                        CarouselItem.saveState(targetPlane);
                                        storyboard.Stop();
                                        CarouselItem.restoreState(targetPlane);
                                        SpotStoryboardsPlaying--;

                                        if (SpotStoryboardsPlaying == 0)
                                        {
                                            MoveCarousel();
                                        }
                                    };

            return storyboard;
        }

        void BeginStoryboard(string name, CarouselItem item)
        {
            // Precondition: NOT PLAYING
            Storyboard storyboard = this.Resources[name] as Storyboard;
            
            try
            {
                Storyboard.SetTargetName(storyboard, item.Name);
                storyboard.Begin();
            }
            catch (Exception e) {
                Console.WriteLine(e.Message);
            }
            SpotStoryboardsPlaying++;
        }

        // Formulas
        int CalculateZ(int spot)
        {
            int pos = spot - CarouselSpotCount / 2;

            return (pos < 0) ? pos : -pos;
        }

        private double FormulaX(int pos)
        {
            pos -= CarouselSpotCount / 2;

            double posAbs = Math.Abs(pos);
            double xFromLimit = VIEWPORT_WIDTH / 4;
            double xToLimit = VIEWPORT_WIDTH / 2 - 10;
            
            // Quadratic:
            //double d = CarouselSpotCount / 2;
            //double den = (d - 1) * (d - 1);
            //double a = (xFromLimit - xToLimit) / den;
            //double b = 2 * d * (xToLimit - xFromLimit) / den;
            //double c = (d * d * xFromLimit + xToLimit - 2 * d * xToLimit) / den;

            //return Math.Sign(pos) * (a * posAbs * posAbs + b * posAbs + c);

            // Linear:
            return Math.Sign(pos)
                   * ( (xToLimit - xFromLimit) * (posAbs - 1) / (CarouselSpotCount / 2 - 1)
                       + xFromLimit );
        }

        private double Scale(int pos)
        {
            pos -= CarouselSpotCount / 2;

            if (pos == 0)
            {
                return 1;
            }
            else
            {
                return 0.8 - Math.Abs(pos * 0.02);
            }
        }

        private double Pitch(int pos)
        {
            pos -= CarouselSpotCount / 2;

            double angFromLimit = 50;
            double angToLimit = 85;

            return Math.Sign(pos)
                * ((angToLimit - angFromLimit) * (Math.Abs(pos) - 1)
                    / (CarouselSpotCount / 2 - 1)
                  + angFromLimit);
        }

        private double OpacityFormula(int pos)
        {
            if (pos == 0 || pos == CarouselSpotCount - 1)
            {
                return 0;
            }
            
            else
            {
                return 1;
            }
        }

        public enum Direction
        {
            Right, Left
        }

        public void Freeze()
        {
            // Block the Carousel
            Canvas.SetZIndex(itemDisplayer, 199);
            OpaqueCover.Visibility = Visibility.Visible;
            Storyboard sb = (Storyboard)this.Resources["FadingBackground"];
            sb.Begin();
        }

        public void UnFreeze()
        {
            // Un-block the Carousel
            Canvas.SetZIndex(itemDisplayer, 201);
            if (!DetailedDisplaying)
            {
                OpaqueCover.Visibility = Visibility.Collapsed;
                Storyboard sb = (Storyboard)this.Resources["FadingBackground"];
                sb.Stop();
            }
        }

        public void Clear()
        {
            DisplayableItems.Clear();

            if (itemDisplayer.Visibility == Visibility.Visible)
            {
                // Instantly restore the background
                Storyboard sb = (Storyboard)this.Resources["FadingBackground"];
                sb.Stop();
                OpaqueCover.Visibility = Visibility.Collapsed;

                // Fade the ItemDisplayer Out
                sb = (Storyboard)this.Resources["ItemDisplayerFadeOut"];
                sb.Begin();
            }

            InitializeVariables();

            leftArrow.Visibility = Visibility.Collapsed;
            rightArrow.Visibility = Visibility.Collapsed;
        }

        void InitializeVariables()
        {
            FirstSpotTaken = -1;
            LastSpotTaken = -1;
            FirstDisplayed = -1;
            LastDisplayed = -1;
            DisplayingItemsCount = 0;
            MovesPending = 0;
            SpotStoryboardsPlaying = 0;
        }
    }
}






